package io.gitee.thinkbungee.crud.mongo.service;

import cn.hutool.core.util.StrUtil;
import io.gitee.thinkbungee.constant.SatelliteConstant;
import io.gitee.thinkbungee.crud.BaseEntity;
import io.gitee.thinkbungee.crud.PageParam;
import io.gitee.thinkbungee.crud.annotation.Query;
import io.gitee.thinkbungee.crud.mongo.bean.MongoPage;
import io.gitee.thinkbungee.crud.mongo.bean.SortBuilder;
import io.gitee.thinkbungee.crud.mongo.criteria.CriteriaWrapper;
import io.gitee.thinkbungee.crud.mongo.entity.SatelliteMongoEntity;
import io.gitee.thinkbungee.crud.mybatis.entity.SatelliteMybatisEntity;
import io.gitee.thinkbungee.enums.BaseOperationEnum;
import io.gitee.thinkbungee.exception.BusinessException;
import io.gitee.thinkbungee.utils.SatelliteUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Sort;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * mongo 基础service
 * <p>
 * 参考 @see SatelliteMybatisBaseService
 * <p>
 * <p>
 * 风险点：
 * MongoCRUDHelper 很多方法都需要传入 class类型， 有的方法可能会包装返回值
 *
 * @author chenyichang
 * @date 2022/4/18
 */
public interface SatelliteMongoBaseService<P extends PageParam, T extends BaseEntity> {

    Logger log = LoggerFactory.getLogger(SatelliteMongoBaseService.class);

    /**
     * 获取mongoCRUDHelper
     */
    MongoCRUDHelper getMongoCRUDHelper();

    /**
     * 返回实体类
     */
    Class<T> getEntityClass();

    //======================拦截器，操作前、后的处理======================//

    /**
     * 操作前处理
     *
     * @param entity    实体对象
     * @param operation 具体操作类型
     * @throws BusinessException 异常，回滚事务
     */
    default void processBeforeOperation(T entity, BaseOperationEnum operation) throws BusinessException {

    }

    /**
     * 操作前处理
     *
     * @param list      实体对象
     * @param operation 具体操作类型
     * @throws BusinessException 异常，回滚事务
     */
    default void processBatchBeforeOperation(List<T> list, BaseOperationEnum operation) throws BusinessException {

    }


    /**
     * 操作后处理
     *
     * @param entity    实体
     * @param operation 具体操作类型
     * @throws BusinessException 异常，回滚事务
     */
    default void processAfterOperation(T entity, BaseOperationEnum operation) throws BusinessException {

    }

    /**
     * 操作后处理
     *
     * @param list      实体
     * @param operation 具体操作类型
     * @throws BusinessException 异常，回滚事务
     */
    default void processBatchAfterOperation(List<T> list, BaseOperationEnum operation) throws BusinessException {

    }


    //=====================包装实体及扩展处理========================//

    /**
     * 包装返回实体
     *
     * @param entity
     * @return
     */
    default T wrapperEntity(T entity) {
        return entity;
    }

    /**
     * 结果处理
     *
     * @param mongoPage 结果
     * @return
     */
    default MongoPage<T> resultHandler(MongoPage<T> mongoPage) {

        return mongoPage;
    }

    /**
     * 扩展查询条件
     *
     * @param param   查询参数
     * @param wrapper 查询条件
     * @return
     */
    default CriteriaWrapper extensionWrapper(P param, CriteriaWrapper wrapper) {
        return wrapper;
    }

    /**
     * 对查询条件进行处理
     *
     * @param wrapper 查询条件
     * @return
     */
    default CriteriaWrapper handlerQueryWrapper(CriteriaWrapper wrapper) {
        return wrapper;
    }

    //====================业务相关============================//

    /**
     * 根据参数拼装查询条件
     *
     * @param param 参数
     * @return
     */
    default CriteriaWrapper getWrapper(P param) {
        CriteriaWrapper wrapper = new CriteriaWrapper();
        //解析param==>queryWrapper
        initWrapper(param, wrapper);
        return wrapper;
    }

    /**
     * 将param的查询条件解析到queryWrapper中
     *
     * @param param   查询条件
     * @param wrapper 封装的查询条件
     */
    default void initWrapper(P param, CriteriaWrapper wrapper) {

        //拿到param的字段查询条件
        Field[] fields = param.getClass().getDeclaredFields();

        //Query注解,没有注解的字段不参与条件
        Arrays.stream(fields).filter(field -> {
            if (field.isAnnotationPresent(Query.class)) {
                Query query = field.getAnnotation(Query.class);
                return query.where();
            }
            return false;
        }).forEach(field -> {
            try {
                boolean flag;
                field.setAccessible(true);

                //判断该属性的值是否为null 或者 空 " "
                if (field.get(param) instanceof String) {
                    flag = StrUtil.isNotBlank((String) field.get(param));
                } else {
                    flag = field.get(param) != null;
                }
                //字段为空，没啥比较的，跳过
                if (!flag) {
                    return;
                }

                //取查询字段,优先取注解上的column值，否则按照驼峰命名转下划线取字段名
                String column = StrUtil.isNotBlank(field.getAnnotation(Query.class).column()) ?
                        field.getAnnotation(Query.class).column() :
                        SatelliteUtils.camelCaseToUnderline(field.getName());

                switch (field.getAnnotation(Query.class).value()) {
                    // EQ 写到默认中
                    // case EQ:
                    //     break;
                    case LIKE:
                        String like = String.valueOf(field.get(param));
                        wrapper.like(column, like);
                        break;
                    case GT:
                        wrapper.gt(column, field.get(param));
                        break;
                    case GTE:
                        wrapper.gte(column, field.get(param));
                        break;
                    case LT:
                        wrapper.lt(column, field.get(param));
                        break;
                    case LTE:
                        wrapper.lte(column, field.get(param));
                        break;
                    case IN:
                        Object value = field.get(param);
                        if (value instanceof List) {
                            wrapper.in(column, (List) value);
                        } else if (value instanceof String) {
                            String[] split = String.valueOf(value).split(SatelliteConstant.SPLIT);
                            wrapper.in(column, split);
                        }
                        break;
                    case BETWEEN:
                        String[] split = String.valueOf(field.get(param)).split(SatelliteConstant.SPLIT);
                        if (split.length == 2) {
                            wrapper.gte(column, split[0])
                                    .lte(column, split[1]);
                        } else if (split.length == 1) {
                            wrapper.gte(column, split[0]);
                        }
                        break;
                    default:
                        wrapper.eq(column, field.get(param));
                }

            } catch (Exception e) {
                log.error("initWrapper error", e);
            }
        });
    }


    //===============提供一些基础的增删改查========================//

    /**
     * 新增数据
     *
     * @param entity 实体
     * @return 是否成功
     * @throws BusinessException 异常
     */
    @Transactional(rollbackFor = Exception.class)
    default String insert(T entity) throws BusinessException {
        //处理前
        processBeforeOperation(entity, BaseOperationEnum.INSERT);


        String result = getMongoCRUDHelper().insert(entity);

        if (StringUtils.isEmpty(result)) {
            throw BusinessException.build(SatelliteConstant.Exception.INSERT_FAILURE);
        }

        //缓存
        refreshCacheFields(entity, BaseOperationEnum.INSERT);

        //处理后
        processAfterOperation(entity, BaseOperationEnum.INSERT);

        return result;
    }


    /**
     * 批量新增
     *
     * @param entities 数据
     * @return
     * @throws BusinessException 异常
     */
    @Transactional(rollbackFor = Exception.class)
    default void insertBatch(List<T> entities) throws BusinessException {
        try {
            getMongoCRUDHelper().insertAll(entities);
        } catch (Exception e) {
            throw BusinessException.build(SatelliteConstant.Exception.INSERT_FAILURE);
        }

        //刷新缓存
        entities.forEach(entity -> refreshCacheFields(entity, BaseOperationEnum.INSERT));

    }

    /**
     * 更新数据
     *
     * @param entity 实体
     * @return
     * @throws BusinessException 异常
     */
    @Transactional(rollbackFor = Exception.class)
    default void update(T entity) throws BusinessException {
        //更新前操作
        processBeforeOperation(entity, BaseOperationEnum.UPDATE);

        //先删除缓存
        if (entity instanceof SatelliteMybatisEntity) {
            SatelliteMongoEntity mEntity = (SatelliteMongoEntity) entity;
            T dbEntity = selectById(mEntity.getId());
            refreshCacheFields(dbEntity, BaseOperationEnum.DELETE);
        }

        //更新
        try {
            getMongoCRUDHelper().updateById(entity);
        } catch (Exception e) {
            throw BusinessException.build(SatelliteConstant.Exception.UPDATE_FAILURE);
        }

        //入缓存
        refreshCacheFields(entity, BaseOperationEnum.UPDATE);

        //更新后操作
        processAfterOperation(entity, BaseOperationEnum.UPDATE);
    }

    /**
     * 删除
     *
     * @param id 主键id
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    default void deleteById(String id) {
        T t = selectById(id);
        if (t == null) {
            throw BusinessException.build(SatelliteConstant.Exception.RECORD_NOT_EXIST);
        }
        processBeforeOperation(t, BaseOperationEnum.DELETE);
        try {
            getMongoCRUDHelper().deleteById(id.toString(), t.getClass());
        } catch (Exception e) {
            throw BusinessException.build(SatelliteConstant.Exception.DELETE_FAILURE);
        }

        refreshCacheFields(t, BaseOperationEnum.DELETE);

        processAfterOperation(t, BaseOperationEnum.DELETE);

    }

    /**
     * 批量删除
     *
     * @param idList id列表
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    default void deleteByIds(Collection<String> idList) {

        List<T> list = getMongoCRUDHelper().findByIds(idList, getEntityClass());
        //操作前处理
        processBatchBeforeOperation(list, BaseOperationEnum.DELETE_BATCH);


        getMongoCRUDHelper().deleteByIds(new ArrayList<>(idList), getEntityClass());

        //缓存
        list.forEach(t -> refreshCacheFields(t, BaseOperationEnum.DELETE_BATCH));
        //操作后处理
        processBatchAfterOperation(list, BaseOperationEnum.DELETE_BATCH);

    }

    /**
     * 根据条件删除
     */
    @Transactional(rollbackFor = Exception.class)
    default void delete(CriteriaWrapper criteriaWrapper) {
        getMongoCRUDHelper().deleteByQuery(criteriaWrapper, getEntityClass());
    }

    /**
     * 查询符合条件的数量
     *
     * @param wrapper
     * @return
     */
    default Long count(CriteriaWrapper wrapper) {
        return getMongoCRUDHelper().count(wrapper, getEntityClass());
    }

    /**
     * 查询符合条件的记录
     *
     * @param wrapper
     * @return
     */
    default List<T> list(CriteriaWrapper wrapper) {
        return getMongoCRUDHelper().findList(wrapper, getEntityClass());
    }

    /**
     * 查询所有
     *
     * @return
     */
    default List<T> findAll() {
        return getMongoCRUDHelper().findAll(getEntityClass());
    }


    /**
     * 通过id查询
     *
     * @param id
     * @return
     */
    default T selectById(String id) {
        return wrapperEntity(getMongoCRUDHelper().findById(id, getEntityClass()));
    }

    /**
     * 根据条件查询
     *
     * @param wrapper 条件
     * @return
     */
    default T selectOne(CriteriaWrapper wrapper) {
        return wrapperEntity(getMongoCRUDHelper().findOne(wrapper, getEntityClass()));
    }

    //
//    default T selectOne(String column, Object value) {
//        List<T> list = list(column, value);
//        if (list.isEmpty()) {
//            return null;
//        }
//        return list.get(0);
//    }
//
//    /**
//     * 查询指定值的数据
//     *
//     * @param column 列
//     * @param value  值
//     * @return
//     */
//    default List<T> list(String column, Object value) {
//        QueryWrapper<T> queryWrapper = new QueryWrapper();
//        queryWrapper.eq(column, value);
//        return getMongoCRUDHelper().selectList(queryWrapper);
//    }

    /**
     * 分页查询所有
     *
     * @param pageParam
     * @return
     */
    default MongoPage<T> page(P pageParam) {
        return page(pageParam, null);
    }

    /**
     * 分页查询，带查询条件
     *
     * @param pageParam 分页参数
     * @param wrapper   查询条件
     * @return
     */
    default MongoPage<T> page(P pageParam, CriteriaWrapper wrapper) {

        MongoPage<T> page = new MongoPage<>();
        page.setCurrent(pageParam.getPageNumber());
        page.setSize(pageParam.getPageSize());

        //mongo根据param排序
        orderNew(page, pageParam);

        //有wrapper自定义查询条件,就不带参数的条件
        if (wrapper != null) {
            return resultHandler(getMongoCRUDHelper().findPage(wrapper, page, getEntityClass()));
        }

        CriteriaWrapper queryWrapper = extensionWrapper(pageParam, getWrapper(pageParam));

        //扩展：对条件进行处理
        handlerQueryWrapper(queryWrapper);

        return resultHandler(getMongoCRUDHelper().findPage(queryWrapper, page, getEntityClass()));
    }

    /**
     * todo 待实现,是否需要做缓存及缓存刷新
     *
     * @param entity
     * @param operation
     */
    default void refreshCacheFields(T entity, BaseOperationEnum operation) {
    }

    /**
     * 设置排序(版本二)
     *
     * @param page      设置排序
     * @param pageParam 查询条件实体
     */
    default void orderNew(MongoPage<T> page, P pageParam) {
        String order = pageParam.getOrder();
        String sort = pageParam.getSort();

        String[] sortSplit;
        String[] orderSplit;

        if (StringUtils.isNotBlank(sort) && StringUtils.isNotBlank(order)) {

            /**
             * 多字段排序逻辑：
             * 都含有分隔符 "," 逗号时：
             * 如果sort字段和order字段一一对应，则一一对应放入orderItem中
             * 如果不对应，则以排序字段order中的第一个排序为准
             */
            if (sort.contains(SatelliteConstant.SPLIT) && order.contains(SatelliteConstant.SPLIT)) {
                sortSplit = sort.split(SatelliteConstant.SPLIT);
                orderSplit = order.split(SatelliteConstant.SPLIT);

                SortBuilder[] sortBuilders = new SortBuilder[sortSplit.length];
                if (sortSplit.length == orderSplit.length) {
                    //多字段的排序，sort和order的分割长度 如果一致，则可以多字段按需排序
                    for (int i = 0; i < orderSplit.length; i++) {
                        //mongo无需将字段转为下划线风格
                        sortBuilders[i] = buildOrder(sortSplit[i], orderSplit[i]);
                    }
                } else {
                    //否则按照order中的第一个排序来排序
                    String order0 = orderSplit[0];
                    for (int i = 0; i < sortSplit.length; i++) {
                        sortBuilders[i] = buildOrder(sortSplit[i], order0);
                    }
                }
                page.addOrder(sortBuilders);
            }
            //当只有排序字段sort有分隔符时
            if (sort.contains(SatelliteConstant.SPLIT) && !order.contains(SatelliteConstant.SPLIT)) {
                List<String> orderList = Arrays.stream(sort.split(SatelliteConstant.SPLIT))
                        .map(SatelliteUtils::camelCaseToUnderline)
                        .collect(Collectors.toList());
                String[] orderColumns = orderList.toArray(new String[]{});

                if (SatelliteConstant.DESC.equalsIgnoreCase(order)) {
                    page.addOrder(SortBuilder.descs(orderColumns));
                } else {
                    //默认也是升序
                    page.addOrder(SortBuilder.ascs(orderColumns));
                }

            }

            //都没有分隔符时
            if (!sort.contains(SatelliteConstant.SPLIT) && !order.contains(SatelliteConstant.SPLIT)) {
                page.addOrder(buildOrder(sort, order));
            }

        }
    }

    /**
     * 构建排序
     *
     * @param column 列
     * @param order  排序
     *               只有order=desc/DESC才是降序，其他默认都是正序
     * @return
     */
    default SortBuilder buildOrder(String column, String order) {
        SortBuilder sortBuilder = new SortBuilder();
        //默认是正序
        if (SatelliteConstant.DESC.equalsIgnoreCase(order)) {
            sortBuilder.add(column, Sort.Direction.DESC);
        } else {
            sortBuilder.add(column, Sort.Direction.ASC);
        }
        return sortBuilder;
    }


}
